﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Fluent;
using NLog.LayoutRenderers;
using Opc.Ua;
using Opc.Ua.Client;
using Org.BouncyCastle.Math.EC;
using SHRIS.MIS.Entity.ScadaBaseInfo;
using SHRIS.MIS.Model.Param.APIExternal;
using ShrisCommunicationCore.OpcUa;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SHRIS.MIS.Service.OpcService
{
    public class WorkshopCommunicationService
    {
        private readonly List<OpcServerDescriptionEntity> _opcServerDescriptionEntities;

        /// <summary>
        /// key=serverName
        /// </summary>
        private Dictionary<Guid, SimpleOpcUaClient> simpleOpcUaClients = new Dictionary<Guid, SimpleOpcUaClient>();


        /// <summary>
        /// 所有Opc 服务器的状态
        /// </summary>
        private List<OpcServerStatusDto> opcServerStatuses=new List<OpcServerStatusDto>();
        private readonly ILogger _logger;

        public WorkshopCommunicationService(List<OpcServerDescriptionEntity> opcServerDescriptionEntities, ILogger logger)
        {
            _opcServerDescriptionEntities = opcServerDescriptionEntities;
            _logger = logger;
            CreateOpcClients();
        } 

        /// <summary>
        /// 创建传入的连接对象
        /// </summary>
        private void CreateOpcClients()
        {
            //创建 opc 客户端
           foreach (var opc in _opcServerDescriptionEntities)
            {
                OpcUaServerDescription opcUaServerDescription = new OpcUaServerDescription();
                opcUaServerDescription.ServerId = opc.Id;
                opcUaServerDescription.ClientName = opc.ClientName;
                opcUaServerDescription.ServerUrl = opc.ServerUrl;
                opcUaServerDescription.Configured = opc.Configured;
                opcUaServerDescription.IsFilePath = opc.IsFilePath;
                opcUaServerDescription.ConfigurationSectionName = opc.ConfigurationSectionName;
                opcUaServerDescription.IsAnonymous = opc.IsAnonymous;
                opcUaServerDescription.UserName = opc.UserName;
                opcUaServerDescription.Password = opc.Password;
                opcUaServerDescription.SecerityPolic = opc.SecerityPolic;
                opcUaServerDescription.IgnoreVaildServerNonce = opc.IgnoreVaildServerNonce;
                opcUaServerDescription.ServerName = opc.ServerName;

                SimpleOpcUaClient sim = null;
                try
                {
                    _logger.LogInformation("SimpleOpcUaClient-Test");
                    sim = new SimpleOpcUaClient(opcUaServerDescription);
                    
                }
                catch(Exception ex)
                {
                    Exception ee = new Exception(ex.Message+"  创建客户端失败： "+ opcUaServerDescription.ServerName+" || "+ opcUaServerDescription.ServerId.ToString());
                    throw ee;
                }
                try
                {
                    simpleOpcUaClients.Add(opcUaServerDescription.ServerId, sim);
                }catch(Exception ex)
                {
                    Exception ee = new Exception(ex.Message + "  添加客户端失败 ");
                    throw ee;
                }
                

                
                //创建连接状态
                OpcServerStatusDto opcServerStatusDto = new OpcServerStatusDto();
                opcServerStatusDto.server = opc;
                opcServerStatusDto.IsConnected = false;
                opcServerStatusDto.StatusString = "Disconnected";
                opcServerStatusDto.UpdateTime = DateTime.Now;
                
                opcServerStatuses.Add(opcServerStatusDto);
            }


           //创建其它连接 to do 

        }

        /// <summary>
        /// 创建OPC 客户端连接
        /// </summary>
        /// <returns></returns>
        public async Task ConnectToServers()
        {
            //创建OPC 客户端
            if(simpleOpcUaClients.Count>0)
            {
                foreach(var sc in simpleOpcUaClients)
                {
                    try
                    {
                        await sc.Value.Connect();
                        sc.Value.OpcStatusChange += Value_OpcStatusChange;
                    }
                    catch(Exception ex)
                    {
                        Log.Error(sc.Value.ServerName+" : "+ex.Message);
                        //simpleOpcUaClients.Remove(sc.Key);
                    }
                }
            }

            //创建其他连接 to do 

        }

        private void Value_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
        {
            //SimpleOpcUaClient
            var opcClient = sender as SimpleOpcUaClient;
            var statusDto = opcServerStatuses.Where(i => i.server.Id == opcClient.ServerId).First();
            if(statusDto!=null)
            {
                statusDto.IsConnected = e.IsConnected;
                statusDto.StatusString = e.Text;
                statusDto.UpdateTime = e.Time;
            }

            
        }


        /// <summary>
        /// 为传入的opcData 地址，选择对应的OPC 服务器，并在对应服务器上创建其DeviceId 的订阅
        /// </summary>
        /// <param name="opcDataSourceEntities">OPC 数据地址，必须在同一个服务器上</param>
        /// <param name="monitoredItemNotificationEventHandler">该组数据对应的回调方法</param>
        /// <returns></returns>
        public bool CreateOpcSubscription(IEnumerable<OpcDataSourceEntity> opcDataSourceEntities,MonitoredItemNotificationEventHandler monitoredItemNotificationEventHandler,string subscriptionName="default")
        {
            var servers = opcDataSourceEntities.Select(i => i.ServerId).Distinct().ToList();
            if (servers.Count==1)
            {
                var deviceCodes = opcDataSourceEntities.Select(i => i.DeviceCode).Distinct().ToList();
                if(deviceCodes.Count==1)
                {
                    SimpleOpcUaClient simpleOpcUaClient = null;
                    bool r = simpleOpcUaClients.TryGetValue(servers[0], out simpleOpcUaClient);
                    if (r && simpleOpcUaClient != null&&simpleOpcUaClient.IsConnected)
                    {
                        List<string> tags = new List<string>();
                        List<string> itemNames = new List<string>();
                        int publishTime = 100;
                        foreach(var dataSource in opcDataSourceEntities)
                        {
                            tags.Add(dataSource.SourceAddress);
                            itemNames.Add(dataSource.Id.ToString());
                            publishTime = dataSource.InternalTime;
                        }
                        try
                        {
                            if(subscriptionName!= "default")
                            {
                                simpleOpcUaClient.CreateSubscription(subscriptionName, publishTime, publishTime,
                                    tags, itemNames, monitoredItemNotificationEventHandler);
                                return true;
                            }
                            else
                            {
                                simpleOpcUaClient.CreateSubscription(deviceCodes[0], publishTime, publishTime,
                                    tags, itemNames, monitoredItemNotificationEventHandler);
                                return true;
                            }
                        }
                        catch(Exception ex)
                        {
                            throw ex;
                        }
                        
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 获取连接到对应opc server 的client 连接
        /// </summary>
        /// <param name="serverId"></param>
        /// <returns></returns>
        public SimpleOpcUaClient GetOpcClient(Guid serverId)
        {
            SimpleOpcUaClient simpleOpcUaClient = null;
            bool r = simpleOpcUaClients.TryGetValue(serverId, out simpleOpcUaClient);
            if(r)
            {
                return simpleOpcUaClient;
            }
            else
            {
                return null;
            }
            
        }


        public IEnumerable<OpcServerStatusDto> GetOpcServerStatus ()
        {
            return opcServerStatuses;
        }


        public bool WriteValue(Guid serverId, string tag, string value)
        {
            SimpleOpcUaClient client = null;
            bool s = simpleOpcUaClients.TryGetValue(serverId, out client);
            if(s)
            {
                return client.WriteNode(tag, value);
            }

            else
            {
                return false;
            }
        }

        public bool WriteValue<T>(Guid serverId, string tag, T value)
        {
            SimpleOpcUaClient client = null;
            bool s = simpleOpcUaClients.TryGetValue(serverId, out client);
            if (s)
            {
                return client.WriteNode(tag, value);
            }

            else
            {
                return false;
            }
        }

        public bool WriteValues(Guid serverId, List<string> tags, List<string> values)
        {
            SimpleOpcUaClient client = null;
            bool s = simpleOpcUaClients.TryGetValue(serverId, out client);
            if (s)
            {
                return client.WriteNodes(tags, values);
            }

            else
            {
                return false;
            }
        }


        public DataValue ReadValue(Guid serverId, string tag)
        {
            SimpleOpcUaClient client = null;
            bool s = simpleOpcUaClients.TryGetValue(serverId, out client);
            if (s)
            {
                return client.ReadNode(tag);
            }

            else
            {
                return null;
            }
        }

        public List<DataValue> ReadValues(Guid serverId, List<string> nodeIds)
        {
            SimpleOpcUaClient client = null;
            bool s = simpleOpcUaClients.TryGetValue(serverId, out client);
            if (s)
            {
                return client.ReadNodes(nodeIds);
            }

            else
            {
                return null;
            }
        }

    }
}
